package com.wuliang.generate.common.core.utils;

import com.wuliang.generate.common.core.annotation.EnableSwaggerConfig;
import com.wuliang.generate.common.core.annotation.WLApplication;
import com.wuliang.generate.common.core.basic.BaseVo;
import com.wuliang.generate.common.core.basic.BasicEntity;
import com.wuliang.generate.common.core.response.ReturnCode;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.reflections.util.ConfigurationBuilder;
import org.springframework.context.annotation.Lazy;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 反射工具类
 * @author WuLiang
 */
@Lazy
@Slf4j
public class ClassUtils {

    /**
     * @param returnCodeFieldName ReturnCode 字段名
     * @return {@link ReturnCode}
     */
    public static ReturnCode getReturnCodeByFieldName(String returnCodeFieldName) {
//        根据返回码名称调用返回码对象
        for (ReturnCode code : ReturnCode.values()) {
            String codeName = code.toString();
            codeName = codeName.replaceAll("_", "");
            if (returnCodeFieldName.contains(codeName)) {
                return code;
            }
        }
        return null;
    }

    /**
     * @return 获取 {@link EnableSwaggerConfig} 标注的类  的限定包名
     */
    public static List<String> getBasePackages() {

        // 反射工具包，指明本项目路径
        log.info("project package was {}", ConfigurationBuilder.build("com.wuliang.generate.response"));
        Reflections reflections = new Reflections(ConfigurationBuilder.build("com.wuliang.generate.response"));
        // 声明一个集合，用来存放包的限定名
        List<String> basePackages = new ArrayList<>();
        // 获取带 EnableSwaggerConfig 注解的类
        Set<Class<?>> classList = reflections.getTypesAnnotatedWith(WLApplication.class);
        for (Class classes : classList) {
            // 获取被标注类的限定包名
            basePackages.add(classes.getPackage().toString());
        }
        return basePackages;
    }

    /**
     * 根据给定对象和另一个对象的class来另一个对象的属性进行 set 操作
     */
    public static <T extends BasicEntity> T setValueToFields(Object value, Class<T> tClass) {
        try {
//        首先获取tClass对象的setter方法
            List<Method> setterMethods = setterMethods(tClass);
//        然后再将value对象转换成Map以便于获取对应的属性名称
            Map<String, Object> valueOfMap = JsonUtils.objectToMap(value);
//        在根据tClass的属性名称获取map集合中对应的Value
            T entity = tClass.newInstance();
            valueOfMap.keySet().forEach(s -> {
                System.out.println("keyName: " + s);
            });
            for (Method setterMethod : setterMethods) {
//                截取set之后的方法名作为map集合的key
                String fieldName = setterMethod.getName().substring(3);
                fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1);
                if (valueOfMap.containsKey(fieldName)) {
                    Object fieldValue = valueOfMap.get(fieldName);
//                    最后依次赋值
                    setterMethod.invoke(entity, fieldValue);
                }
            }
            return entity;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取实体类中的Setter方法
     *
     * @param tClass 实体类
     * @return Setter方法列表
     */
    private static List<Method> setterMethods(Class<? extends BasicEntity> tClass) {
        List<Method> setterMethods = new ArrayList<>();
        for (Method method : tClass.getMethods()) {
            if (method.getName().startsWith("set")) {
                setterMethods.add(method);
            }
        }
        return setterMethods;
    }

    /**
     * 获取方法描述信息
     *
     * @param controllerMethod 控制器方法
     * @return 方法描述
     */
    public static String getMethodDescription(Class<?> controllerMethod) {
        Annotation annotation = controllerMethod.getAnnotation(ApiOperation.class);
        for (Field field : annotation.annotationType().getFields()) {
            System.out.println(field.getName());
        }
        return null;
    }

    /**
     * 获取对象中所有字段，包括子对象
     *
     * @param aClass    要获取字段的对象
     * @param fromClass 根对象
     * @return 所有字段名称
     */
    public static List<String> getClassAllField(Class<?> aClass, Class<?> fromClass) {
        return getClassAllField(aClass, fromClass, null);
    }

    /**
     * 获取对象中所有字段，包括子对象
     *
     * @param aClass          要获取字段的对象
     * @param fromClass       根对象
     * @param objectFieldName 子对象在根对象中的名称
     * @return 所有字段名称
     */
    public static List<String> getClassAllField(Class<?> aClass, Class<?> fromClass, String objectFieldName) {
        // 声明字段名称列表
        List<String> fieldList = new ArrayList<>();
        // 获取私有属性列表
        for (Field declaredField : aClass.getDeclaredFields()) {
            // 获取私有属性名称
            String fieldName = declaredField.getName();
            // 如果根对象的名称与子对象的名称不等，那么就在字段前追加 子对象在根对象中的名称
            if (!aClass.getName().equals(fromClass.getName())) {
                fieldName = objectFieldName + "." + declaredField.getName();
            }
            // 如果子对象是继承与 基础传输数据类型的话
            if (BaseVo.class.isAssignableFrom(declaredField.getType())) {
                // 递归获取子对象的属性名称
                fieldList.addAll(getClassAllField(declaredField.getType(), aClass, declaredField.getName()));
            } else {
                // 否则将当前属性名称追加到字段名称列表中
                fieldList.add(fieldName);
            }
        }
        return fieldList;
    }

    /**
     * 反射调用 getter 方法
     *
     * @param o         被调用的对象
     * @param fieldName 被调用的字段名
     * @return 被调用的字段的值
     */
    public static Object invokeGetterMethod(Object o, String fieldName) {
        // 声明被调用对象的 class
        Class<?> cla = o.getClass();
        // 获取属性的get方法，注意将首字母转大写
        String getMethodName = "get" + ((char) (fieldName.charAt(0) - 32)) + fieldName.substring(1);
        // 如果属性名称全转大写后和ID相等
        if ("ID".equals(fieldName.toUpperCase())) {
            // 那么get方法名则为 getID
            getMethodName = "getID";
        }
        // 声明调用结果
        Object invokeResult = null;
        try {
            // 声明方法对象
            Method getMethod;
            // 如果被调用的属性名包含  .   则截取到  .  之前的字段
            if (fieldName.contains(".")) {
                getMethodName = getMethodName.substring(0, getMethodName.indexOf("."));
            }
            // 根据 getter 方法名称赋值到方法对象
            getMethod = cla.getMethod(getMethodName);
            // 调用 getter 方法
            invokeResult = getMethod.invoke(o);
            // 如果被调用的是子对象，并且get后的结果不为空，则递归
            if (fieldName.contains(".") && invokeResult != null) {
                // 如果调用结果为 Map 类型，则截取 . 后的字段名称，再调用 map.get方法来获取值
                if (invokeResult instanceof Map) {
                    return ((Map<String, Object>) invokeResult).get(fieldName.substring(fieldName.indexOf(".") + 1));
                } else if (invokeResult instanceof List || invokeResult instanceof Set) {
                    return null;
                }
                // 如果不是集合类型，则递归调用
                return invokeGetterMethod(invokeResult, fieldName.substring(fieldName.indexOf(".") + 1));
            }
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return invokeResult;
    }

    public static void invokeSetterMethod(Object o, String fieldName) {
        // 声明被调用对象的 class
        Class<?> cla = o.getClass();
        // 获取属性的get方法，注意将首字母转大写
        String getMethodName = "set" + ((char) (fieldName.charAt(0) - 32)) + fieldName.substring(1);
        // 如果属性名称全转大写后和ID相等
        try {
            // 声明方法对象
            Method getMethod;
            // 根据 getter 方法名称赋值到方法对象
            getMethod = cla.getMethod(getMethodName);
            // 调用 getter 方法
            getMethod.invoke(o);
            // 如果被调用的是子对象，并且get后的结果不为空，则递归
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}
